package middleware

import (
	"context"
	"fmt"
	"strings"
	"wms/dao/mysql"
	"wms/dao/redis"
	"wms/define"
	"wms/models"
	"wms/pkg/jwt"

	"github.com/gin-gonic/gin"
)

// LoginAuthCheck 基于JWT的认证中间件
func LoginAuthCheck() func(c *gin.Context) {
	return func(c *gin.Context) {
		// 客户端携带Token有三种方式 1.放在请求头 2.放在请求体 3.放在URI
		// 这里假设Token放在Header的Authorization中，并使用Bearer开头
		// 这里的具体实现方式要依据你的实际业务情况决定
		authHeader := c.Request.Header.Get("Authorization")
		if authHeader == "" {
			define.ResponseErrorWithMsg(c, define.CodeInvalidToken, "请求头缺少Auth Token")
			c.Abort()
			return
		}
		// 按空格分割
		parts := strings.SplitN(authHeader, " ", 2)
		if !(len(parts) == 2 && parts[0] == "Bearer") {
			define.ResponseErrorWithMsg(c, define.CodeInvalidToken, "Token格式不对")
			c.Abort()
			return
		}
		// parts[1]是获取到的tokenString，我们使用之前定义好的解析JWT的函数来解析它
		mc, err := jwt.ParseToken(parts[1])
		if err != nil {
			define.ResponseError(c, define.CodeInvalidToken)
			c.Abort()
			return
		}

		if mc.RoleIdentity == "" {
			c.Abort()
			define.ResponseError(c, define.CodeRoleError)
			return
		}
		// 从redis查询是否存在该角色，判断是否是超管
		adminRoleKey := define.RedisRoleAdminPrefix + mc.RoleIdentity
		isAdmin, err := redis.RDB.Get(context.Background(), adminRoleKey).Result()
		if err != nil {
			// redis不存在该角色
			var rb models.RoleBasic
			tx := mysql.GetRoleBasicByIdentity(mc.RoleIdentity)
			if err := tx.Find(&rb).Error; err != nil {
				c.Abort()
				define.ResponseError(c, define.CodeServerBusy)
				return
			}
			// 当前角色缓存redis,保存一周
			redis.RDB.Set(context.Background(), adminRoleKey, rb.IsAdmin, define.RedisExpireDurationWeek)
			if rb.IsAdmin == 1 {
				isAdmin = "1"
			} else {
				isAdmin = "0"
			}
		} else {
			// redis查到数据，再续一周
			redis.RDB.Expire(context.Background(), adminRoleKey, define.RedisExpireDurationWeek)
		}
		if isAdmin == "1" {
			mc.IsAdmin = true
		} else {
			mc.IsAdmin = false
		}
		c.Set("MyClaims", mc)
		c.Next()
	}
}

// FuncAuthCheck 功能权限认证中间件
func FuncAuthCheck() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 不是超管，判断是否有相关权限
		mc := c.MustGet("MyClaims").(*define.MyClaims)
		// 可操作的函数 key
		funcKey := define.RedisFuncPrefix + mc.RoleIdentity

		if mc.RoleIdentity != "1" {
			keyExists, _ := redis.RDB.Exists(context.Background(), funcKey).Result()

			if keyExists > 0 {
				// redis 存在对应角色的权限分配表
				redis.RDB.Expire(context.Background(), funcKey, define.RedisExpireDurationWeek)
				// redis 查找该表中是否有存在，当前访问的 uri 的前缀
				uris, _ := redis.RDB.HGetAll(context.Background(), funcKey).Result()

				//fieldExists, _ := redis.RDB.HExists(context.Background(), funcKey, c.Request.RequestURI).Result()

				// 不存在中断请求
				uri := c.Request.RequestURI
				ok := false
				for funcUri, _ := range uris {
					if strings.HasPrefix(uri, funcUri) {
						ok = true
						break
					}
				}
				if !ok {
					fmt.Println("redis 存在用户的权限表，但没有权限访问:", c.Request.RequestURI)
					// 权限不存在，非法访问
					c.Abort()
					define.ResponseError(c, define.CodeRequestError)
					return
				}
			} else {
				// key 不存在，从 mysql 查询返回，并保存 redis
				// 1. 从 mysql 获取角色对应的所有 uris
				uris := make([]*define.AuthFuncUriReply, 0)
				tx, err := mysql.GetFuncUrisByRoleIdentity(mc.RoleIdentity)
				if err != nil {
					c.Abort()
					define.ResponseError(c, define.CodeServerBusy)
					return
				}
				if err := tx.Scan(&uris).Error; err != nil {
					c.Abort()
					define.ResponseError(c, define.CodeServerBusy)
					return
				}
				// 2. 构造映射，存入 redis
				data := make(map[string]interface{})
				for _, v := range uris {
					data[v.Uri] = "1"
				}
				fmt.Println("redis 不存在, mysql 读出当前角色的 uris", data)

				err = redis.RDB.HSet(context.Background(), funcKey, data).Err()
				if err != nil {
					c.Abort()
					define.ResponseError(c, define.CodeServerBusy)
					return
				}
				redis.RDB.Expire(context.Background(), funcKey, define.RedisExpireDurationWeek)
			}
		}
	}
}
